home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / AppsToGo / DTS.Lib / CIconControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  15.1 KB  |  591 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         cciconcontrol.c
  5. ** Written by:      Eric Soldan
  6. **
  7. ** Copyright © 1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* CIcon control theory of operation:
  20. **
  21. ** The CIcon control stores the id passed to it in the refCon field of the control.
  22. ** When the defProc is called, it draws the 'cicn' smartly using the GWLayers CIcon
  23. ** utilities.  (These utilities can draw the 'cicn', even if PlotCIcon isn't available.)
  24. **
  25. ** When the CIcon control is hit, it draws that 'cicn' number plus 1.  So, if your id
  26. ** for the control is 1000, 'cicn' #1000 is the up-state of the button, and #1001 is
  27. ** the down state.  It doesn't just invert the base 'cicn', as that doesn't allow for
  28. ** some effects, such as buttons that push in when clicked on.
  29. **
  30. ** If there is no base+1 'cicn', then the base 'cicn' is inverted for hilite==1 state.
  31. **
  32. ** For hilite==255, if there is a base+2 'cicn', it is drawn, otherwise the base 'cicn' is used.
  33. **
  34. ** base, base+1, and base+2 are slight simplifications.  The actual case is base, base+n, base+n+n,
  35. ** where n is the number of 'cicn's necessary to tile the control rect.
  36. ** 'cicn' #base is upper-left, #base+1 is just to the right, etc.
  37. **
  38. ** A variant usage of the CIcon control is a family of buttons.  Instead of tracking
  39. ** the single CIcon control, and having it revert back to the unclicked state after
  40. ** release, this variation is used for buttons that push in and stick.
  41. **
  42. ** The below rules apply for variant #1 of the CIcon control:
  43. **
  44. ** • The user clicks on a CIcon control.  As usual, the hilite==1 state is displayed.
  45. ** • In addition to the above, any CIcon control in the window with the same family
  46. **   is redrawn in hilite==0 state, and it's value is turned off.  The CIcon control
  47. **   that was clicked on has it's value immediately set to 1.
  48. ** • Any variant #1 CIcon control when drawn in hilite==0 state draws as if it is in
  49. **   hilite==1 state.  This means that once the control is clicked on, it's stuck in
  50. **   hilite==1 state.
  51. ** • The hilite==255 rules still apply.
  52. ** • To allow for a value of 1 to be stored, the max value for the control has to be
  53. **   at least 1.
  54. ** • A third state for the control is optional.  The third state is for double-clicking
  55. **   the control down.  Many applications want to make a tool either a one-shot use
  56. **   or permanent use tool.  By allowing double-clicking on the CIcon control, we can
  57. **   accomplish this.
  58. ** • The value for a double-clicked CIcon control is 2.  This indicates to the
  59. **   control that it should draw the 'cicn' #base+n+n.
  60. ** • To allow for a value of 2, the max value of the control has to be at least 2.
  61. ** • The max value for the CIcon control is used as the family number.  The family number
  62. **   is used to determine which other CIcon control to turn off when this control is
  63. **   clicked down.  Due to this other usage of the max value, bit-0 of the max value
  64. **   is used to determine if double-clicking is allowed.
  65. **       odd  family number (max value), single-clicking only.
  66. **       even family number (max value), double-clicking feature enabled.
  67. */
  68.  
  69.  
  70.  
  71. /*****************************************************************************/
  72.  
  73.  
  74.  
  75. #ifndef __CICONCONTROL__
  76. #include "CIconControl.h"
  77. #endif
  78.  
  79. #ifndef __CONTROLS__
  80. #include <Controls.h>
  81. #endif
  82.  
  83. #ifndef __DTSLib__
  84. #include "DTS.Lib.h"
  85. #endif
  86.  
  87. #ifndef __ERRORS__
  88. #include <Errors.h>
  89. #endif
  90.  
  91. #ifndef __FONTS__
  92. #include <Fonts.h>
  93. #endif
  94.  
  95. #ifndef __GWLAYERS__
  96. #include "GWLayers.h"
  97. #endif
  98.  
  99. #ifndef __MEMORY__
  100. #include <Memory.h>
  101. #endif
  102.  
  103. #ifndef __PACKAGES__
  104. #include <Packages.h>
  105. #endif
  106.  
  107. #ifndef __RESOURCES__
  108. #include <Resources.h>
  109. #endif
  110.  
  111. #ifndef __STRINGUTILS__
  112. #include "StringUtils.h"
  113. #endif
  114.  
  115. #ifndef __UTILITIES__
  116. #include "Utilities.h"
  117. #endif
  118.  
  119.  
  120.  
  121. /*****************************************************************************/
  122.  
  123.  
  124.  
  125. #if defined(powerc) || defined (__powerc)
  126. #pragma options align=mac68k
  127. #endif
  128. typedef struct cdefRsrcJMP {
  129.     long    jsrInst;
  130.     long    moveInst;
  131.     short    jmpInst;
  132.     long    jmpAddress;
  133. } cdefRsrcJMP;
  134. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  135. #if defined(powerc) || defined(__powerc)
  136. #pragma options align=reset
  137. #endif
  138.  
  139. typedef struct CbtnInfo {
  140.     short        resIconID;
  141.     short        hicons, vicons;
  142.     short        variant;
  143. } CbtnInfo;
  144. typedef CbtnInfo *CbtnInfoPtr, **CbtnInfoHndl;
  145.  
  146.  
  147.  
  148. /*****************************************************************************/
  149.  
  150.  
  151.  
  152. short    gCIconCtl = rCIconCtl;
  153.  
  154. extern ControlHandle    gWhichCtlHit;
  155. extern Boolean            gWhichCtlDbl;
  156. extern Boolean            gWhichCtlTracking;
  157.  
  158. static pascal long        CCIconCtl   (short varCode, ControlHandle ctl, short msg, long parm);
  159. static pascal void        CCIconAction(ControlHandle ctl, short part);
  160. static pascal void        NoRect(GrafVerb verb, Rect *r);
  161.  
  162. static cdefRsrcJMPHndl    gCDEF;
  163.  
  164.  
  165.  
  166. /*****************************************************************************/
  167. /*****************************************************************************/
  168. /*****************************************************************************/
  169.  
  170.  
  171.  
  172. #pragma segment Controls
  173. static pascal long    CCIconCtl(short varCode, ControlHandle ctl, short msg, long parm)
  174. {
  175.     Rect                viewRct, r;
  176.     short                resIconID;
  177.     CbtnInfoHndl        info;
  178.     short                hicons, vicons, i, h, v, variant, oldCtlVal;
  179.     short                thisHilite, keepHilite, fnum;
  180.     CIconHandle            icon;
  181.     ControlHandle        cc;
  182.     WindowPtr            ww;
  183.     short                txFont, txSize, txMode;
  184.     Style                txFace;
  185.     QDProcs                qdp;
  186.     QDProcsPtr            oldqdp;
  187.     FontInfo            finfo;
  188.     ControlStyleInfo    cinfo;
  189.     LayerObj            wlayer, blayer;
  190.     Handle                hh;
  191.  
  192.     static QDRectUPP    qdrupp;
  193.  
  194.     if (!(info = (CbtnInfoHndl)(*ctl)->contrlRfCon)) return(0);
  195.  
  196.     viewRct    = (*ctl)->contrlRect;
  197.     thisHilite = (*ctl)->contrlHilite;
  198.     variant    = ((*info)->variant & 0xFFF7);
  199.  
  200.     switch (msg) {
  201.  
  202.         case drawCntl:
  203.  
  204.             resIconID = (*info)->resIconID;
  205.             hicons    = (*info)->hicons;
  206.             vicons    = (*info)->vicons;
  207.  
  208.             if (variant & 0x01) {
  209.                 if (thisHilite < 2)
  210.                     thisHilite = (*ctl)->contrlValue;
  211.                 if (thisHilite == 2)
  212.                     thisHilite = 255;
  213.             }
  214.  
  215.             wlayer = nil;
  216.  
  217.             switch (thisHilite) {
  218.                 case 1:
  219.                 case 255:
  220.                     i = (thisHilite == 1) ? (resIconID + vicons * hicons) :
  221.                                             (resIconID + 2 * vicons * hicons);
  222.                     SetResLoad(false);
  223.                     if (!GetResource('cicn', i)) i = resIconID;
  224.                     if (GetResource('cicn', i)) {
  225.                         (*info)->resIconID = i;
  226.                         keepHilite = (*ctl)->contrlHilite;
  227.                         (*ctl)->contrlHilite = 99;
  228.                         CCIconCtl(varCode, ctl, msg, parm);
  229.                         (*ctl)->contrlHilite = keepHilite;
  230.                         (*info)->resIconID = resIconID;
  231.                         if (thisHilite == 1)
  232.                             if (i == resIconID)
  233.                                 InvertRect(&viewRct);
  234.                                     /* If there is no hilite==1 'cicn', invert the base 'cicn'. */
  235.                     }
  236.                     SetResLoad(true);
  237.                     break;
  238.                 default:
  239.                     SetResLoad(false);
  240.                     hh = GetResource('cicn', resIconID);
  241.                     SetResLoad(true);
  242.                     if (!hh) {
  243.                         EraseRect(&viewRct);
  244.                         FrameRect(&viewRct);
  245.                         break;
  246.                     }
  247.                     for (i = v = 0; v < vicons;) {
  248.                         r.top    = viewRct.top + 64 * v;
  249.                         r.bottom = r.top  + 64;
  250.                         if (++v == vicons)
  251.                             r.bottom = viewRct.bottom;
  252.                         if (variant & 0x02) {
  253.                             NewLayer(&wlayer, nil, nil, (*ctl)->contrlOwner, 0, 0);
  254.                             (*wlayer)->dstRect = viewRct;            /* Minimize the size of the offscreen. */
  255.                             NewLayer(&blayer, wlayer, nil, nil, 0, 0);
  256.                             SetLayerWorld(blayer);
  257.                             EraseRect(&viewRct);
  258.                             InvalLayer(wlayer, viewRct, false);
  259.                         }
  260.                         for (h = 0; h < hicons; ++i) {
  261.                             r.left  = viewRct.left + 64 * h;
  262.                             r.right = r.left + 64;
  263.                             if (++h == hicons)
  264.                                 r.right = viewRct.right;
  265.                             icon = ReadCIcon(resIconID + i);
  266.                             if (icon) {
  267.                                 DrawCIcon(icon, r);
  268.                                 KillCIcon(icon);
  269.                             }
  270.                         }
  271.                     }
  272.                     break;
  273.             }
  274.  
  275.             GetPort(&ww);
  276.             txFont = ww->txFont;
  277.             txFace = ww->txFace;
  278.             txSize = ww->txSize;
  279.             if (!((*info)->variant & useWFont)) {
  280.                 TextFont(systemFont);
  281.                 TextFace(normal);
  282.                 TextSize(0);
  283.             }
  284.             else {
  285.                 if (GetControlStyle(ctl, &cinfo)) {
  286.                     TextFace(cinfo.fontStyle);
  287.                     fnum = systemFont;
  288.                     if (cinfo.font[0])
  289.                         GetFNum(cinfo.font, &fnum);
  290.                     TextFont(fnum);
  291.                     TextSize(cinfo.fontSize);
  292.                 }
  293.             }
  294.  
  295.             oldqdp = ww->grafProcs;
  296.             if (oldqdp)
  297.                 BlockMove(oldqdp, &qdp, sizeof(QDProcs));
  298.             else
  299.                 SetStdProcs(&qdp);
  300.  
  301.             if (!qdrupp) {
  302. #if USES68KINLINES
  303.                 qdrupp = (QDRectUPP)NoRect;
  304. #else
  305.                 qdrupp = NewQDRectProc(NoRect);
  306. #endif
  307.             }
  308.             qdp.rectProc = qdrupp;
  309.  
  310.             ww->grafProcs = &qdp;
  311.             txMode = ww->txMode;
  312.             TextMode(srcOr);
  313.  
  314.             HLock((Handle)ctl);
  315.             GetFontInfo(&finfo);
  316.             viewRct.top += (finfo.ascent + finfo.descent) / 2;
  317.             if (viewRct.top < viewRct.bottom)
  318.                 TETextBox((*ctl)->contrlTitle + 1, (*ctl)->contrlTitle[0], &viewRct, teCenter);
  319.             HUnlock((Handle)ctl);
  320.  
  321.             ww->grafProcs = oldqdp;
  322.             TextMode(txMode);
  323.  
  324.             TextFont(txFont);
  325.             TextFace(txFace);
  326.             TextSize(txSize);
  327.  
  328.             if (wlayer) {
  329.                 UpdateLayer(wlayer);
  330.                 ResetLayerWorld(blayer);
  331.                 DisposeThisAndBelowLayers(wlayer);
  332.             }
  333.  
  334.             break;
  335.  
  336.         case testCntl:
  337.  
  338.             oldCtlVal = (*ctl)->contrlValue;
  339.  
  340.             if ((*ctl)->contrlHilite == 255) return(0);
  341.                 /* Control disabled, so no click.  (We probably don't get called in this case. */
  342.  
  343.             if (!(variant & 0x01)) return(PtInRect(*(Point *)&parm, &viewRct));
  344.                 /* Don't make user call WhichControl unless the variant
  345.                 ** is for double-clicking.  (Then it has to be called.) */
  346.  
  347.             if (ctl != gWhichCtlHit) return(0);
  348.                 /* WhichControl already found the control hit.  Unless it is the one
  349.                 ** found by WhichControl, consider it unhit. */
  350.  
  351.             if (gWhichCtlTracking) return(1);
  352.                 /* We already handled it, but the control manager is insistent. */
  353.  
  354.             if (!(*ctl)->contrlValue) {            /* Turn off any other controls in this family. */
  355.                 ww = (*ctl)->contrlOwner;
  356.                 for (cc = nil; (cc = CCIconNext(ww, cc, 1, true)) != nil;) {
  357.                     if (cc != ctl) {
  358.                         if (((*cc)->contrlMax & 0xFFF0) == ((*ctl)->contrlMax & 0xFFF0)) {
  359.                             if ((*cc)->contrlValue) {
  360.                                 (*cc)->contrlValue  = 0;
  361.                                 (*cc)->contrlHilite = 0;
  362.                                 CCIconCtl(varCode, cc, drawCntl, 0);
  363.                             }
  364.                         }
  365.                     }
  366.                 }
  367.             }
  368.  
  369.             (*ctl)->contrlValue = 1;
  370.             if (gWhichCtlDbl)                            /* If user double-clicked... */
  371.                 if (!((*ctl)->contrlMax & 0x01))        /* If double-clicking allowed... */
  372.                     (*ctl)->contrlValue = 2;
  373.  
  374.             if (oldCtlVal != (*ctl)->contrlValue)
  375.                 CCIconCtl(varCode, ctl, drawCntl, 0);
  376.  
  377.             gWhichCtlTracking = true;
  378.             return(1);
  379.  
  380.             break;
  381.  
  382.         case calcCRgns:
  383.         case calcCntlRgn:
  384.             if (msg == calcCRgns)
  385.                 parm &= 0x00FFFFFF;
  386.             RectRgn((RgnHandle)parm, &viewRct);
  387.             break;
  388.  
  389.         case initCntl:
  390.             break;
  391.  
  392.         case dispCntl:
  393.             DisposeHandle((Handle)(*ctl)->contrlRfCon);
  394.             break;
  395.  
  396.         case posCntl:
  397.             break;
  398.  
  399.         case thumbCntl:
  400.             break;
  401.  
  402.         case dragCntl:
  403.             break;
  404.  
  405.         case autoTrack:
  406.             break;
  407.     }
  408.  
  409.     return(0);
  410. }
  411.  
  412.  
  413.  
  414. /*****************************************************************************/
  415.  
  416.  
  417.  
  418. #pragma segment Controls
  419. ControlHandle    CCIconNew(WindowPtr window, Rect *r, StringPtr title, Boolean vis, short val,
  420.                           short min, short max, short viewID, short refcon)
  421. {
  422.     WindowPtr        oldPort;
  423.     Rect            viewRct;
  424.     Boolean            err;
  425.     ControlHandle    viewCtl;
  426.     CbtnInfoHndl    info;
  427.     short            hicons, vicons;
  428.  
  429.     static ControlActionUPP    cupp;
  430.     static ControlDefUPP    cdefupp;
  431.  
  432.     GetPort(&oldPort);
  433.     SetPort(window);
  434.  
  435.     viewRct = *r;
  436.     viewCtl = nil;
  437.     info    = nil;
  438.  
  439.     err = false;
  440.  
  441.     if (!gCDEF) {
  442.         gCDEF = (cdefRsrcJMPHndl)GetResource('CDEF', (viewID / 16));
  443.         if (gCDEF) {
  444.             if (!cdefupp) {
  445.                 cdefupp = NewControlDefProc(CCIconCtl);
  446.             }
  447.             (*gCDEF)->jmpAddress = (long)cdefupp;
  448.             FlushInstructionCache();    /* Make sure that instruction caches don't kill us. */
  449.         }
  450.         else err = true;
  451.     }
  452.  
  453.     if (!err) {
  454.         info = (CbtnInfoHndl)NewHandleClear(sizeof(CbtnInfo));
  455.         if (info) {
  456.             hicons = (viewRct.right - viewRct.left - 1) / 64 + 1;
  457.             if (hicons < 1) hicons = 1;
  458.             vicons = (viewRct.bottom - viewRct.top - 1) / 64 + 1;
  459.             if (vicons < 1) vicons = 1;
  460.     
  461.             (*info)->resIconID = refcon;
  462.             (*info)->hicons    = hicons;
  463.             (*info)->vicons    = vicons;
  464.             (*info)->variant   = (viewID & 0x0F);
  465.         }
  466.         else err = true;
  467.     }
  468.  
  469.     if (!err) {
  470.         viewCtl = NewControl(window, &viewRct, title, vis, val, min, max, viewID, (long)info);
  471.         if (viewCtl) {
  472.             if (!cupp) cupp = NewControlActionProc(CCIconAction);
  473.             SetControlAction(viewCtl, cupp);
  474.         }
  475.         else
  476.             err = true;
  477.     }
  478.  
  479.     SetPort(oldPort);
  480.  
  481.     if (err) {        /* Oops.  Somebody wasn't happy. */
  482.         if (info)
  483.             DisposeHandle((Handle)info);
  484.                 /* viewCtl exists only if no error, so we just have to possibly clean up info. */
  485.     }
  486.  
  487.     return(viewCtl);
  488. }
  489.  
  490.  
  491.  
  492. /*****************************************************************************/
  493.  
  494.  
  495.  
  496. #pragma segment Controls
  497. ControlHandle    CCIconNext(WindowPtr window, ControlHandle ctl, short dir, Boolean justActive)
  498. {
  499.     ControlHandle    nextCtl, priorCtl;
  500.  
  501.     if (!window) return(nil);
  502.     if (!gCDEF)  return(nil);
  503.  
  504.     if (dir > 0) {
  505.         if (!ctl)
  506.             ctl = ((WindowPeek)window)->controlList;
  507.         else
  508.             ctl = (*ctl)->nextControl;
  509.         while (ctl) {
  510.             if ((!justActive) || ((*ctl)->contrlVis)) {
  511.                 if ((!justActive) || ((*ctl)->contrlHilite != 255)) {
  512.                     if (*(*ctl)->contrlDefProc == *(Handle)gCDEF)
  513.                         return(ctl);
  514.                             /* The handle may be locked, which means that the hi-bit
  515.                             ** may be on, thus invalidating the compare.  Dereference the
  516.                             ** handles to get rid of this possibility. */
  517.                 }
  518.             }
  519.             ctl = (*ctl)->nextControl;
  520.         }
  521.         return(ctl);
  522.     }
  523.  
  524.     nextCtl = ((WindowPeek)window)->controlList;
  525.     for (priorCtl = nil; ;nextCtl = (*nextCtl)->nextControl) {
  526.         if ((!nextCtl) || (nextCtl == ctl)) return(priorCtl);
  527.         if ((!justActive) || ((*nextCtl)->contrlVis)) {
  528.             if ((!justActive) || ((*nextCtl)->contrlHilite != 255)) {
  529.                 if (*(*ctl)->contrlDefProc == *(Handle)gCDEF)
  530.                     priorCtl = nextCtl;
  531.                         /* The handle may be locked, which means that the hi-bit
  532.                         ** may be on, thus invalidating the compare.  Dereference the
  533.                         ** handles to get rid of this possibility. */
  534.             }
  535.         }
  536.     }
  537. }
  538.  
  539.  
  540.  
  541. /*****************************************************************************/
  542.  
  543.  
  544.  
  545. #pragma segment Controls
  546. Boolean    IsCIconCtl(ControlHandle ctl)
  547. {
  548.     if (ctl)
  549.         if (*(*ctl)->contrlDefProc == *(Handle)gCDEF)
  550.             return(true);
  551.                 /* The handle may be locked, which means that the hi-bit
  552.                 ** may be on, thus invalidating the compare.  Dereference the
  553.                 ** handles to get rid of this possibility. */
  554.  
  555.     return(false);
  556. }
  557.  
  558.  
  559.  
  560. /*****************************************************************************/
  561.  
  562.  
  563.  
  564. #pragma segment Controls
  565. static pascal void    CCIconAction(ControlHandle ctl, short part)
  566. {
  567.     static short    lastPart = 0;
  568.  
  569.     if (lastPart != part) {
  570.         lastPart = part;
  571.         CCIconCtl(0, ctl, drawCntl, part);
  572.     }
  573. }
  574.  
  575.  
  576.  
  577. /*****************************************************************************/
  578.  
  579.  
  580.  
  581. #pragma segment Controls
  582. static pascal void    NoRect(GrafVerb verb, Rect *r)
  583. {
  584. #ifndef __MWERKS__ 
  585. #pragma unused (verb, r)
  586. #endif
  587. }
  588.  
  589.  
  590.  
  591.